home *** CD-ROM | disk | FTP | other *** search
/ PC World 2008 March / PCWorld_2008-03_cd.bin / v cisle / mobiDVD / MobiDVD-1.0.0.6.exe / xulrunner / components / nsHelperAppDlg.js < prev    next >
Text File  |  2007-07-27  |  40KB  |  1,013 lines

  1. /*
  2. //@line 43 "/cygdrive/d/builds/tinderbox/XR-Trunk/WINNT_5.2_Depend/mozilla/toolkit/mozapps/downloads/src/nsHelperAppDlg.js.in"
  3. */
  4.  
  5. /* This file implements the nsIHelperAppLauncherDialog interface.
  6.  *
  7.  * The implementation consists of a JavaScript "class" named nsUnknownContentTypeDialog,
  8.  * comprised of:
  9.  *   - a JS constructor function
  10.  *   - a prototype providing all the interface methods and implementation stuff
  11.  *
  12.  * In addition, this file implements an nsIModule object that registers the
  13.  * nsUnknownContentTypeDialog component.
  14.  */
  15.  
  16.  
  17. /* ctor
  18.  */
  19. function nsUnknownContentTypeDialog() {
  20.     // Initialize data properties.
  21.     this.mLauncher = null;
  22.     this.mContext  = null;
  23.     this.mSourcePath = null;
  24.     this.chosenApp = null;
  25.     this.givenDefaultApp = false;
  26.     this.updateSelf = true;
  27.     this.mTitle    = "";
  28. }
  29.  
  30. nsUnknownContentTypeDialog.prototype = {
  31.     nsIMIMEInfo  : Components.interfaces.nsIMIMEInfo,
  32.  
  33.     // This "class" supports nsIHelperAppLauncherDialog, and nsISupports.
  34.     QueryInterface: function (iid) {
  35.         if (!iid.equals(Components.interfaces.nsIHelperAppLauncherDialog) &&
  36.             !iid.equals(Components.interfaces.nsISupports)) {
  37.             throw Components.results.NS_ERROR_NO_INTERFACE;
  38.         }
  39.         return this;
  40.     },
  41.  
  42.     // ---------- nsIHelperAppLauncherDialog methods ----------
  43.  
  44.     // show: Open XUL dialog using window watcher.  Since the dialog is not
  45.     //       modal, it needs to be a top level window and the way to open
  46.     //       one of those is via that route).
  47.     show: function(aLauncher, aContext, aReason)  {
  48.       this.mLauncher = aLauncher;
  49.       this.mContext  = aContext;
  50.       // Display the dialog using the Window Watcher interface.
  51.       
  52.       var ir = aContext.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
  53.       var dwi = ir.getInterface(Components.interfaces.nsIDOMWindowInternal);
  54.       var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
  55.                 .getService(Components.interfaces.nsIWindowWatcher);
  56.       this.mDialog = ww.openWindow(dwi,
  57.                                    "chrome://mozapps/content/downloads/unknownContentType.xul",
  58.                                    null,
  59.                                    "chrome,centerscreen,titlebar,dialog=yes,dependent",
  60.                                    null);
  61.       // Hook this object to the dialog.
  62.       this.mDialog.dialog = this;
  63.       
  64.       // Hook up utility functions. 
  65.       this.getSpecialFolderKey = this.mDialog.getSpecialFolderKey;
  66.       
  67.       // Watch for error notifications.
  68.       this.progressListener.helperAppDlg = this;
  69.       this.mLauncher.setWebProgressListener(this.progressListener);
  70.     },
  71.  
  72.     // promptForSaveToFile:  Display file picker dialog and return selected file.
  73.     //                       This is called by the External Helper App Service
  74.     //                       after the ucth dialog calls |saveToDisk| with a null
  75.     //                       target filename (no target, therefore user must pick).
  76.     //
  77.     //                       Alternatively, if the user has selected to have all
  78.     //                       files download to a specific location, return that
  79.     //                       location and don't ask via the dialog. 
  80.     //
  81.     // Note - this function is called without a dialog, so it cannot access any part
  82.     // of the dialog XUL as other functions on this object do. 
  83.     promptForSaveToFile: function(aLauncher, aContext, aDefaultFile, aSuggestedFileExtension) {
  84.       var result = "";
  85.       
  86.       this.mLauncher = aLauncher;
  87.  
  88.       // If the user is always downloading to the same location, the default download
  89.       // folder is stored in preferences. If a value is found stored, use that 
  90.       // automatically and don't ask via a dialog. 
  91.       var prefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);
  92.       var autodownload = prefs.getBoolPref("browser.download.useDownloadDir");
  93.       if (autodownload) {
  94.         function getSpecialFolderKey(aFolderType) 
  95.         {
  96.           if (aFolderType == "Desktop")
  97.             return "Desk";
  98.         
  99.           if (aFolderType != "Downloads")
  100.             throw "ASSERTION FAILED: folder type should be 'Desktop' or 'Downloads'";
  101.         
  102. //@line 143 "/cygdrive/d/builds/tinderbox/XR-Trunk/WINNT_5.2_Depend/mozilla/toolkit/mozapps/downloads/src/nsHelperAppDlg.js.in"
  103.           return "Pers";
  104. //@line 151 "/cygdrive/d/builds/tinderbox/XR-Trunk/WINNT_5.2_Depend/mozilla/toolkit/mozapps/downloads/src/nsHelperAppDlg.js.in"
  105.         }
  106.         
  107.         function getDownloadsFolder(aFolder)
  108.         {
  109.           var fileLocator = Components.classes["@mozilla.org/file/directory_service;1"].getService(Components.interfaces.nsIProperties);
  110.  
  111.           var dir = fileLocator.get(getSpecialFolderKey(aFolder), Components.interfaces.nsILocalFile);
  112.           
  113.           var bundle = Components.classes["@mozilla.org/intl/stringbundle;1"].getService(Components.interfaces.nsIStringBundleService);
  114.           bundle = bundle.createBundle("chrome://mozapps/locale/downloads/unknownContentType.properties");
  115.  
  116.           var description = bundle.GetStringFromName("myDownloads");
  117.           if (aFolder != "Desktop")
  118.             dir.append(description);
  119.             
  120.           return dir;
  121.         }
  122.  
  123.         var defaultFolder = null;
  124.         switch (prefs.getIntPref("browser.download.folderList")) {
  125.         case 0:
  126.           defaultFolder = getDownloadsFolder("Desktop");
  127.           break;
  128.         case 1:
  129.           defaultFolder = getDownloadsFolder("Downloads");
  130.           break;
  131.         case 2:
  132.           defaultFolder = prefs.getComplexValue("browser.download.dir", Components.interfaces.nsILocalFile);
  133.           break;
  134.         }
  135.         
  136.         result = this.validateLeafName(defaultFolder, aDefaultFile, aSuggestedFileExtension);
  137.       }
  138.       
  139.       if (!result) {
  140.         // Use file picker to show dialog.
  141.         var nsIFilePicker = Components.interfaces.nsIFilePicker;
  142.         var picker = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
  143.  
  144.         var bundle = Components.classes["@mozilla.org/intl/stringbundle;1"].getService(Components.interfaces.nsIStringBundleService);
  145.         bundle = bundle.createBundle("chrome://mozapps/locale/downloads/unknownContentType.properties");
  146.  
  147.         var windowTitle = bundle.GetStringFromName("saveDialogTitle");
  148.         var parent = aContext.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIDOMWindowInternal);
  149.         picker.init(parent, windowTitle, nsIFilePicker.modeSave);
  150.         picker.defaultString = aDefaultFile;
  151.  
  152.         if (aSuggestedFileExtension) {
  153.           // aSuggestedFileExtension includes the period, so strip it
  154.           picker.defaultExtension = aSuggestedFileExtension.substring(1);
  155.         } 
  156.         else {
  157.           try {
  158.             picker.defaultExtension = this.mLauncher.MIMEInfo.primaryExtension;
  159.           } 
  160.           catch (ex) { }
  161.         }
  162.  
  163.         var wildCardExtension = "*";
  164.         if (aSuggestedFileExtension) {
  165.           wildCardExtension += aSuggestedFileExtension;
  166.           picker.appendFilter(this.mLauncher.MIMEInfo.description, wildCardExtension);
  167.         }
  168.  
  169.         picker.appendFilters( nsIFilePicker.filterAll );
  170.  
  171.         // Pull in the user's preferences and get the default download directory.
  172.         var prefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);
  173.         try {
  174.           var startDir = prefs.getComplexValue("browser.download.dir", Components.interfaces.nsILocalFile);
  175.           if (startDir.exists()) {
  176.             picker.displayDirectory = startDir;
  177.           }
  178.         } 
  179.         catch(exception) { }
  180.  
  181.         var dlgResult = picker.show();
  182.  
  183.         if (dlgResult == nsIFilePicker.returnCancel) {
  184.           // null result means user cancelled.
  185.           return null;
  186.         }
  187.  
  188.  
  189.         // Be sure to save the directory the user chose through the Save As... 
  190.         // dialog  as the new browser.download.dir
  191.         result = picker.file;
  192.  
  193.         if (result) {
  194.           try {
  195.             // Remove the file so that it's not there when we ensure non-existence later;
  196.             // this is safe because for the file to exist, the user would have had to
  197.             // confirm that he wanted the file overwritten.
  198.             if (result.exists())
  199.               result.remove(false);
  200.           }
  201.           catch (e) { }
  202.           var newDir = result.parent;
  203.           prefs.setComplexValue("browser.download.dir", Components.interfaces.nsILocalFile, newDir);
  204.           result = this.validateLeafName(newDir, result.leafName, null);
  205.         }
  206.       }
  207.       return result;
  208.     },
  209.  
  210.     /**
  211.      * Ensures that a local folder/file combination does not already exist in
  212.      * the file system (or finds such a combination with a reasonably similar
  213.      * leaf name), creates the corresponding file, and returns it.
  214.      *
  215.      * @param   aLocalFile
  216.      *          the folder where the file resides
  217.      * @param   aLeafName
  218.      *          the string name of the file (may be empty if no name is known,
  219.      *          in which case a name will be chosen)
  220.      * @param   aFileExt
  221.      *          the extension of the file, if one is known; this will be ignored
  222.      *          if aLeafName is non-empty
  223.      * @returns nsILocalFile
  224.      *          the created file
  225.      */
  226.     validateLeafName: function (aLocalFile, aLeafName, aFileExt)
  227.     {
  228.       if (!aLocalFile || !aLocalFile.exists())
  229.         return null;
  230.  
  231.       // Remove any leading periods, since we don't want to save hidden files
  232.       // automatically.
  233.       aLeafName = aLeafName.replace(/^\.+/, "");
  234.  
  235.       if (aLeafName == "")
  236.         aLeafName = "unnamed" + (aFileExt ? "." + aFileExt : "");
  237.       aLocalFile.append(aLeafName);
  238.  
  239.       this.makeFileUnique(aLocalFile);
  240.  
  241.       if (aLocalFile.isExecutable() && !this.mLauncher.targetFile.isExecutable()) {
  242.         var f = aLocalFile.clone();
  243.         aLocalFile.leafName = aLocalFile.leafName + "." + this.mLauncher.MIMEInfo.primaryExtension; 
  244.  
  245.         f.remove(false);
  246.         this.makeFileUnique(aLocalFile);
  247.       }
  248.  
  249.       return aLocalFile;
  250.     },
  251.  
  252.     /**
  253.      * Generates and returns a uniquely-named file from aLocalFile.  If
  254.      * aLocalFile does not exist, it will be the file returned; otherwise, a
  255.      * file whose name is similar to that of aLocalFile will be returned.
  256.      */
  257.     makeFileUnique: function (aLocalFile)
  258.     {
  259.       try {
  260.         // Note - this code is identical to that in 
  261.         //   toolkit/content/contentAreaUtils.js.
  262.         // If you are updating this code, update that code too! We can't share code
  263.         // here since this is called in a js component. 
  264.         var collisionCount = 0;
  265.         while (aLocalFile.exists()) {
  266.           collisionCount++;
  267.           if (collisionCount == 1) {
  268.             // Append "(2)" before the last dot in (or at the end of) the filename
  269.             // special case .ext.gz etc files so we don't wind up with .tar(2).gz
  270.             if (aLocalFile.leafName.match(/\.[^\.]{1,3}\.(gz|bz2|Z)$/i)) {
  271.               aLocalFile.leafName = aLocalFile.leafName.replace(/\.[^\.]{1,3}\.(gz|bz2|Z)$/i, "(2)$&");
  272.             }
  273.             else {
  274.               aLocalFile.leafName = aLocalFile.leafName.replace(/(\.[^\.]*)?$/, "(2)$&");
  275.             }
  276.           }
  277.           else {
  278.             // replace the last (n) in the filename with (n+1)
  279.             aLocalFile.leafName = aLocalFile.leafName.replace(/^(.*\()\d+\)/, "$1" + (collisionCount+1) + ")");
  280.           }
  281.         }
  282.         aLocalFile.create(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0600);
  283.       }
  284.       catch (e) {
  285.         dump("*** exception in validateLeafName: " + e + "\n");
  286.         if (aLocalFile.leafName == "" || aLocalFile.isDirectory()) {
  287.           aLocalFile.append("unnamed");
  288.           if (aLocalFile.exists())
  289.             aLocalFile.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0600);
  290.         }
  291.       }
  292.     },
  293.     
  294.     // ---------- implementation methods ----------
  295.  
  296.     // Web progress listener so we can detect errors while mLauncher is
  297.     // streaming the data to a temporary file.
  298.     progressListener: {
  299.         // Implementation properties.
  300.         helperAppDlg: null,
  301.  
  302.         // nsIWebProgressListener methods.
  303.         // Look for error notifications and display alert to user.
  304.         onStatusChange: function( aWebProgress, aRequest, aStatus, aMessage ) {
  305.             if ( aStatus != Components.results.NS_OK ) {
  306.                 // Get prompt service.
  307.                 var prompter = Components.classes[ "@mozilla.org/embedcomp/prompt-service;1" ]
  308.                                    .getService( Components.interfaces.nsIPromptService );
  309.                 // Display error alert (using text supplied by back-end).
  310.                 prompter.alert( this.dialog, this.helperAppDlg.mTitle, aMessage );
  311.  
  312.                 // Close the dialog.
  313.                 this.helperAppDlg.onCancel();
  314.                 if ( this.helperAppDlg.mDialog ) {
  315.                     this.helperAppDlg.mDialog.close();
  316.                 }
  317.             }
  318.         },
  319.  
  320.         // Ignore onProgressChange, onProgressChange64, onStateChange, onLocationChange, onSecurityChange, and onRefreshAttempted notifications.
  321.         onProgressChange: function( aWebProgress,
  322.                                     aRequest,
  323.                                     aCurSelfProgress,
  324.                                     aMaxSelfProgress,
  325.                                     aCurTotalProgress,
  326.                                     aMaxTotalProgress ) {
  327.         },
  328.  
  329.         onProgressChange64: function( aWebProgress,
  330.                                       aRequest,
  331.                                       aCurSelfProgress,
  332.                                       aMaxSelfProgress,
  333.                                       aCurTotalProgress,
  334.                                       aMaxTotalProgress ) {
  335.         },
  336.  
  337.  
  338.  
  339.         onStateChange: function( aWebProgress, aRequest, aStateFlags, aStatus ) {
  340.         },
  341.  
  342.         onLocationChange: function( aWebProgress, aRequest, aLocation ) {
  343.         },
  344.  
  345.         onSecurityChange: function( aWebProgress, aRequest, state ) {
  346.         },
  347.  
  348.         onRefreshAttempted: function( aWebProgress, aURI, aDelay, aSameURI ) {
  349.           return true;
  350.     }
  351.     },
  352.  
  353.     // initDialog:  Fill various dialog fields with initial content.
  354.     initDialog : function() {
  355.       // Put file name in window title.
  356.       var suggestedFileName = this.mLauncher.suggestedFileName;
  357.  
  358.       // Some URIs do not implement nsIURL, so we can't just QI.
  359.       var url   = this.mLauncher.source;
  360.       var fname = "";
  361.       this.mSourcePath = url.prePath;
  362.       try {
  363.           url = url.QueryInterface( Components.interfaces.nsIURL );
  364.           // A url, use file name from it.
  365.           fname = url.fileName;
  366.           this.mSourcePath += url.directory;
  367.       } catch (ex) {
  368.           // A generic uri, use path.
  369.           fname = url.path;
  370.           this.mSourcePath += url.path;
  371.       }
  372.  
  373.       if (suggestedFileName)
  374.         fname = suggestedFileName;
  375.       
  376.       var displayName = fname.replace(/ +/g, " ");
  377.  
  378.       this.mTitle = this.dialogElement("strings").getFormattedString("title", [displayName]);
  379.       this.mDialog.document.title = this.mTitle;
  380.  
  381.       // Put content type, filename and location into intro.
  382.       this.initIntro(url, fname, displayName);
  383.  
  384.       var iconString = "moz-icon://" + fname + "?size=16&contentType=" + this.mLauncher.MIMEInfo.MIMEType;
  385.       this.dialogElement("contentTypeImage").setAttribute("src", iconString);
  386.  
  387.       // if always-save and is-executable and no-handler
  388.       // then set up simple ui
  389.       var mimeType = this.mLauncher.MIMEInfo.MIMEType;
  390.       var shouldntRememberChoice = (mimeType == "application/octet-stream" || 
  391.                                     mimeType == "application/x-msdownload" ||
  392.                                     this.mLauncher.targetFile.isExecutable());
  393.       if (shouldntRememberChoice && !this.openWithDefaultOK()) {
  394.         // hide featured choice 
  395.         this.mDialog.document.getElementById("normalBox").collapsed = "true";
  396.         // show basic choice 
  397.         this.mDialog.document.getElementById("basicBox").collapsed = "false";
  398.         // change button labels
  399.         this.mDialog.document.documentElement.getButton("accept").label = this.dialogElement("strings").getString("unknownAccept.label");
  400.         this.mDialog.document.documentElement.getButton("cancel").label = this.dialogElement("strings").getString("unknownCancel.label");
  401.         // hide other handler
  402.         this.mDialog.document.getElementById("openHandler").collapsed = "true";
  403.         // set save as the selected option
  404.         this.dialogElement("mode").selectedItem = this.dialogElement("save");
  405.       }
  406.       else {
  407.         this.initAppAndSaveToDiskValues();
  408.  
  409.         // Initialize "always ask me" box. This should always be disabled
  410.         // and set to true for the ambiguous type application/octet-stream.
  411.         // We don't also check for application/x-msdownload here since we
  412.         // want users to be able to autodownload .exe files. 
  413.         var rememberChoice = this.dialogElement("rememberChoice");
  414.  
  415. //@line 481 "/cygdrive/d/builds/tinderbox/XR-Trunk/WINNT_5.2_Depend/mozilla/toolkit/mozapps/downloads/src/nsHelperAppDlg.js.in"
  416.         if (shouldntRememberChoice) {
  417.           rememberChoice.checked = false;
  418.           rememberChoice.disabled = true;
  419.         }
  420.         else {
  421.           rememberChoice.checked = !this.mLauncher.MIMEInfo.alwaysAskBeforeHandling;
  422.         }
  423.         this.toggleRememberChoice(rememberChoice);
  424.  
  425.         // XXXben - menulist won't init properly, hack. 
  426.         var openHandler = this.dialogElement("openHandler");
  427.         openHandler.parentNode.removeChild(openHandler);
  428.         var openHandlerBox = this.dialogElement("openHandlerBox");
  429.         openHandlerBox.appendChild(openHandler);
  430.       }
  431.  
  432.       this.mDialog.setTimeout("dialog.postShowCallback()", 0);
  433.       
  434.       this.mDialog.document.documentElement.getButton("accept").disabled = true;
  435.       const nsITimer = Components.interfaces.nsITimer;
  436.       this._timer = Components.classes["@mozilla.org/timer;1"]
  437.                               .createInstance(nsITimer);
  438.       this._timer.initWithCallback(this, 250, nsITimer.TYPE_ONE_SHOT);
  439.     },
  440.     
  441.     _timer: null,
  442.     notify: function (aTimer) {
  443.       try { // The user may have already canceled the dialog.
  444.         if (!this._blurred)
  445.           this.mDialog.document.documentElement.getButton('accept').disabled = false;
  446.       } catch (ex) {}
  447.       this._delayExpired = true;
  448.       this._timer = null; // the timer won't release us, so we have to release it
  449.     },
  450.     
  451.     postShowCallback: function () {
  452.       this.mDialog.sizeToContent();
  453.  
  454.       // Set initial focus
  455.       this.dialogElement("mode").focus();
  456.     },
  457.  
  458.     // initIntro:
  459.     initIntro: function(url, filename, displayname) {
  460.         this.dialogElement( "location" ).value = displayname;
  461.         this.dialogElement( "location" ).setAttribute("realname", filename);
  462.         this.dialogElement( "location" ).setAttribute("tooltiptext", displayname);
  463.  
  464.         // if mSourcePath is a local file, then let's use the pretty path name instead of an ugly
  465.         // url...
  466.         var pathString = this.mSourcePath;
  467.         try 
  468.         {
  469.           var fileURL = url.QueryInterface(Components.interfaces.nsIFileURL);
  470.           if (fileURL)
  471.           {
  472.             var fileObject = fileURL.file;
  473.             if (fileObject)
  474.             {
  475.               var parentObject = fileObject.parent;
  476.               if (parentObject)
  477.               {
  478.                 pathString = parentObject.path;
  479.               }
  480.             }
  481.           }
  482.         } catch(ex) {}
  483.  
  484.         if (pathString == this.mSourcePath)
  485.         {
  486.           // wasn't a fileURL
  487.           var tmpurl = url.clone(); // don't want to change the real url
  488.           try {
  489.             tmpurl.userPass = "";
  490.           } catch (ex) {}
  491.           pathString = tmpurl.prePath;
  492.         }
  493.  
  494.         // Set the location text, which is separate from the intro text so it can be cropped
  495.         var location = this.dialogElement( "source" );
  496.         location.value = pathString;
  497.         location.setAttribute("tooltiptext", this.mSourcePath);
  498.         
  499.         // Show the type of file. 
  500.         var type = this.dialogElement("type");
  501.         var mimeInfo = this.mLauncher.MIMEInfo;
  502.         
  503.         // 1. Try to use the pretty description of the type, if one is available.
  504.         var typeString = mimeInfo.description;
  505.         
  506.         if (typeString == "") {
  507.           // 2. If there is none, use the extension to identify the file, e.g. "ZIP file"
  508.           var primaryExtension = "";
  509.           try {
  510.             primaryExtension = mimeInfo.primaryExtension;
  511.           }
  512.           catch (ex) {
  513.           }
  514.           if (primaryExtension != "")
  515.             typeString = this.dialogElement("strings").getFormattedString("fileType", [primaryExtension.toUpperCase()]);
  516.           // 3. If we can't even do that, just give up and show the MIME type. 
  517.           else
  518.             typeString = mimeInfo.MIMEType;
  519.         }
  520.         
  521.         type.value = typeString;
  522.     },
  523.     
  524.     _blurred: false,
  525.     _delayExpired: false, 
  526.     onBlur: function(aEvent) {
  527.       if (aEvent.target != this.mDialog.document)
  528.         return;
  529.       this._blurred = true;
  530.       this.mDialog.document.documentElement.getButton("accept").disabled = true;
  531.     },
  532.     
  533.     onFocus: function(aEvent) {
  534.       if (aEvent.target != this.mDialog.document)
  535.         return;
  536.       this._blurred = false;
  537.       if (this._delayExpired) {
  538.         var script = "document.documentElement.getButton('accept').disabled = false";
  539.         this.mDialog.setTimeout(script, 250);
  540.       }
  541.     },
  542.  
  543.     // Returns true if opening the default application makes sense.
  544.     openWithDefaultOK: function() {
  545.         var result;
  546.  
  547.         // The checking is different on Windows...
  548. //@line 614 "/cygdrive/d/builds/tinderbox/XR-Trunk/WINNT_5.2_Depend/mozilla/toolkit/mozapps/downloads/src/nsHelperAppDlg.js.in"
  549.         // Windows presents some special cases.
  550.         // We need to prevent use of "system default" when the file is
  551.         // executable (so the user doesn't launch nasty programs downloaded
  552.         // from the web), and, enable use of "system default" if it isn't
  553.         // executable (because we will prompt the user for the default app
  554.         // in that case).
  555.         
  556.         // Need to get temporary file and check for executable-ness.
  557.         var tmpFile = this.mLauncher.targetFile;
  558.         
  559.         //  Default is Ok if the file isn't executable (and vice-versa).
  560.         return !tmpFile.isExecutable();
  561. //@line 632 "/cygdrive/d/builds/tinderbox/XR-Trunk/WINNT_5.2_Depend/mozilla/toolkit/mozapps/downloads/src/nsHelperAppDlg.js.in"
  562.     },
  563.     
  564.     // Set "default" application description field.
  565.     initDefaultApp: function() {
  566.       // Use description, if we can get one.
  567.       var desc = this.mLauncher.MIMEInfo.defaultDescription;
  568.       if (desc) {
  569.         var defaultApp = this.dialogElement("strings").getFormattedString("defaultApp", [desc]);
  570.         this.dialogElement("defaultHandler").label = defaultApp;
  571.       }
  572.       else {
  573.         this.dialogElement("modeDeck").setAttribute("selectedIndex", "1");
  574.         // Hide the default handler item too, in case the user picks a 
  575.         // custom handler at a later date which triggers the menulist to show.
  576.         this.dialogElement("defaultHandler").hidden = true;
  577.       }
  578.     },
  579.  
  580.     // getPath:
  581.     getPath: function (aFile) {
  582. //@line 655 "/cygdrive/d/builds/tinderbox/XR-Trunk/WINNT_5.2_Depend/mozilla/toolkit/mozapps/downloads/src/nsHelperAppDlg.js.in"
  583.       return aFile.path;
  584. //@line 657 "/cygdrive/d/builds/tinderbox/XR-Trunk/WINNT_5.2_Depend/mozilla/toolkit/mozapps/downloads/src/nsHelperAppDlg.js.in"
  585.     },
  586.  
  587.     // initAppAndSaveToDiskValues:
  588.     initAppAndSaveToDiskValues: function() {
  589.       var modeGroup = this.dialogElement("mode");
  590.  
  591.       // We don't let users open .exe files or random binary data directly 
  592.       // from the browser at the moment because of security concerns. 
  593.       var openWithDefaultOK = this.openWithDefaultOK();
  594.       var mimeType = this.mLauncher.MIMEInfo.MIMEType;
  595.       if (this.mLauncher.targetFile.isExecutable() || (
  596.           (mimeType == "application/octet-stream" ||
  597.            mimeType == "application/x-msdownload") && 
  598.            !openWithDefaultOK)) {
  599.         this.dialogElement("open").disabled = true;
  600.         var openHandler = this.dialogElement("openHandler");
  601.         openHandler.disabled = true;
  602.         openHandler.selectedItem = null;
  603.         modeGroup.selectedItem = this.dialogElement("save");
  604.         return;
  605.       }
  606.     
  607.       // Fill in helper app info, if there is any.
  608.       try {
  609.         this.chosenApp =
  610.           this.mLauncher.MIMEInfo.preferredApplicationHandler
  611.               .QueryInterface(Components.interfaces.nsILocalHandlerApp);
  612.       } catch (e) {
  613.         this.chosenApp = null;
  614.       }
  615.       // Initialize "default application" field.
  616.       this.initDefaultApp();
  617.  
  618.       var otherHandler = this.dialogElement("otherHandler");
  619.               
  620.       // Fill application name textbox.
  621.       if (this.chosenApp && this.chosenApp.executable && 
  622.           this.chosenApp.executable.path) {
  623.         otherHandler.setAttribute("path",
  624.                                   this.getPath(this.chosenApp.executable));
  625.         otherHandler.label = this.chosenApp.executable.leafName;
  626.         otherHandler.hidden = false;
  627.       }
  628.  
  629.       var useDefault = this.dialogElement("useSystemDefault");
  630.       var openHandler = this.dialogElement("openHandler");
  631.       openHandler.selectedIndex = 0;
  632.  
  633.       if (this.mLauncher.MIMEInfo.preferredAction == this.nsIMIMEInfo.useSystemDefault) {
  634.         // Open (using system default).
  635.         modeGroup.selectedItem = this.dialogElement("open");
  636.       } else if (this.mLauncher.MIMEInfo.preferredAction == this.nsIMIMEInfo.useHelperApp) {
  637.         // Open with given helper app.
  638.         modeGroup.selectedItem = this.dialogElement("open");
  639.         openHandler.selectedIndex = 1;
  640.       } else {
  641.         // Save to disk.
  642.         modeGroup.selectedItem = this.dialogElement("save");
  643.       }
  644.       
  645.       // If we don't have a "default app" then disable that choice.
  646.       if (!openWithDefaultOK) {
  647.         var useDefault = this.dialogElement("defaultHandler");
  648.         var isSelected = useDefault.selected;
  649.         
  650.         // Disable that choice.
  651.         useDefault.hidden = true;
  652.         // If that's the default, then switch to "save to disk."
  653.         if (isSelected) {
  654.           openHandler.selectedIndex = 1;
  655.           modeGroup.selectedItem = this.dialogElement("save");
  656.         }
  657.       }
  658.       
  659.       // otherHandler is always disabled on Mac
  660. //@line 736 "/cygdrive/d/builds/tinderbox/XR-Trunk/WINNT_5.2_Depend/mozilla/toolkit/mozapps/downloads/src/nsHelperAppDlg.js.in"
  661.       otherHandler.nextSibling.hidden = otherHandler.nextSibling.nextSibling.hidden = false;
  662. //@line 738 "/cygdrive/d/builds/tinderbox/XR-Trunk/WINNT_5.2_Depend/mozilla/toolkit/mozapps/downloads/src/nsHelperAppDlg.js.in"
  663.       this.updateOKButton();
  664.     },
  665.  
  666.     // Returns the user-selected application
  667.     helperAppChoice: function() {
  668.       return this.chosenApp;
  669.     },
  670.     
  671.     get saveToDisk() {
  672.       return this.dialogElement("save").selected;
  673.     },
  674.     
  675.     get useOtherHandler() {
  676.       return this.dialogElement("open").selected && this.dialogElement("openHandler").selectedIndex == 1;
  677.     },
  678.     
  679.     get useSystemDefault() {
  680.       return this.dialogElement("open").selected && this.dialogElement("openHandler").selectedIndex == 0;
  681.     },
  682.     
  683.     toggleRememberChoice: function (aCheckbox) {
  684.         this.dialogElement("settingsChange").hidden = !aCheckbox.checked;
  685.         this.mDialog.sizeToContent();
  686.     },
  687.     
  688.     openHandlerCommand: function () {
  689.       var openHandler = this.dialogElement("openHandler");
  690.       if (openHandler.selectedItem.id == "choose")
  691.         this.chooseApp();
  692.       else
  693.         openHandler.setAttribute("lastSelectedItemID", openHandler.selectedItem.id);
  694.     },
  695.  
  696.     updateOKButton: function() {
  697.       var ok = false;
  698.       if (this.dialogElement("save").selected) {
  699.         // This is always OK.
  700.         ok = true;
  701.       } 
  702.       else if (this.dialogElement("open").selected) {
  703.         switch (this.dialogElement("openHandler").selectedIndex) {
  704.         case 0:
  705.           // No app need be specified in this case.
  706.           ok = true;
  707.           break;
  708.         case 1:
  709.           // only enable the OK button if we have a default app to use or if 
  710.           // the user chose an app....
  711.           ok = this.chosenApp || /\S/.test(this.dialogElement("otherHandler").getAttribute("path")); 
  712.         break;
  713.         }
  714.       }
  715.  
  716.       // Enable Ok button if ok to press.
  717.       this.mDialog.document.documentElement.getButton("accept").disabled = !ok;
  718.     },
  719.     
  720.     // Returns true iff the user-specified helper app has been modified.
  721.     appChanged: function() {
  722.       return this.helperAppChoice() != this.mLauncher.MIMEInfo.preferredApplicationHandler;
  723.     },
  724.  
  725.     updateMIMEInfo: function() {
  726.       var needUpdate = false;
  727.       // If current selection differs from what's in the mime info object,
  728.       // then we need to update.
  729.       if (this.saveToDisk) {
  730.         needUpdate = this.mLauncher.MIMEInfo.preferredAction != this.nsIMIMEInfo.saveToDisk;
  731.         if (needUpdate)
  732.           this.mLauncher.MIMEInfo.preferredAction = this.nsIMIMEInfo.saveToDisk;
  733.       } 
  734.       else if (this.useSystemDefault) {
  735.         needUpdate = this.mLauncher.MIMEInfo.preferredAction != this.nsIMIMEInfo.useSystemDefault;
  736.         if (needUpdate)
  737.           this.mLauncher.MIMEInfo.preferredAction = this.nsIMIMEInfo.useSystemDefault;
  738.       } 
  739.       else {
  740.         // For "open with", we need to check both preferred action and whether the user chose
  741.         // a new app.
  742.         needUpdate = this.mLauncher.MIMEInfo.preferredAction != this.nsIMIMEInfo.useHelperApp || this.appChanged();
  743.         if (needUpdate) {
  744.           this.mLauncher.MIMEInfo.preferredAction = this.nsIMIMEInfo.useHelperApp;
  745.           // App may have changed - Update application
  746.           var app = this.helperAppChoice();
  747.           this.mLauncher.MIMEInfo.preferredApplicationHandler = app;
  748.         }
  749.       }
  750.       // We will also need to update if the "always ask" flag has changed.
  751.       needUpdate = needUpdate || this.mLauncher.MIMEInfo.alwaysAskBeforeHandling != (!this.dialogElement("rememberChoice").checked);
  752.  
  753.       // One last special case: If the input "always ask" flag was false, then we always
  754.       // update.  In that case we are displaying the helper app dialog for the first
  755.       // time for this mime type and we need to store the user's action in the mimeTypes.rdf
  756.       // data source (whether that action has changed or not; if it didn't change, then we need
  757.       // to store the "always ask" flag so the helper app dialog will or won't display
  758.       // next time, per the user's selection).
  759.       needUpdate = needUpdate || !this.mLauncher.MIMEInfo.alwaysAskBeforeHandling;
  760.  
  761.       // Make sure mime info has updated setting for the "always ask" flag.
  762.       this.mLauncher.MIMEInfo.alwaysAskBeforeHandling = !this.dialogElement("rememberChoice").checked;
  763.  
  764.       return needUpdate;        
  765.     },
  766.     
  767.     // See if the user changed things, and if so, update the
  768.     // mimeTypes.rdf entry for this mime type.
  769.     updateHelperAppPref: function() {
  770.       var ha = new this.mDialog.HelperApps();
  771.       ha.updateTypeInfo(this.mLauncher.MIMEInfo);
  772.       ha.destroy();
  773.     },
  774.     
  775.     // onOK:
  776.     onOK: function() {
  777.       // Verify typed app path, if necessary.
  778.       if (this.useOtherHandler) {
  779.         var helperApp = this.helperAppChoice();
  780.         if (!helperApp || !helperApp.executable ||
  781.             !helperApp.executable.exists()) {
  782.           // Show alert and try again.        
  783.           var bundle = this.dialogElement("strings");                    
  784.           var msg = bundle.getFormattedString("badApp", [this.dialogElement("otherHandler").path]);
  785.           var svc = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService(Components.interfaces.nsIPromptService);
  786.           svc.alert(this.mDialog, bundle.getString("badApp.title"), msg);
  787.  
  788.           // Disable the OK button.
  789.           this.mDialog.document.documentElement.getButton("accept").disabled = true;
  790.           this.dialogElement("mode").focus();          
  791.  
  792.           // Clear chosen application.
  793.           this.chosenApp = null;
  794.  
  795.           // Leave dialog up.
  796.           return false;
  797.         }
  798.       }
  799.         
  800.       // Remove our web progress listener (a progress dialog will be
  801.       // taking over).
  802.       this.mLauncher.setWebProgressListener(null);
  803.  
  804.       // saveToDisk and launchWithApplication can return errors in 
  805.       // certain circumstances (e.g. The user clicks cancel in the
  806.       // "Save to Disk" dialog. In those cases, we don't want to
  807.       // update the helper application preferences in the RDF file.
  808.       try {
  809.         var needUpdate = this.updateMIMEInfo();
  810.         
  811.         if (this.dialogElement("save").selected) {
  812.           // If we're using a default download location, create a path
  813.           // for the file to be saved to to pass to |saveToDisk| - otherwise
  814.           // we must ask the user to pick a save name.
  815.  
  816. //@line 905 "/cygdrive/d/builds/tinderbox/XR-Trunk/WINNT_5.2_Depend/mozilla/toolkit/mozapps/downloads/src/nsHelperAppDlg.js.in"
  817.           // Since saveToDisk may open a file picker and therefore block this routine,
  818.           // we should only call it once the dialog is closed.
  819.           var _delayedSaveToDisk = function(aSelf) {
  820.             aSelf.mLauncher.saveToDisk(null, false);
  821.           }
  822.           this.mDialog.opener.setTimeout(_delayedSaveToDisk, 0, this);
  823.         }
  824.         else
  825.           this.mLauncher.launchWithApplication(null, false);
  826.  
  827.         // Update user pref for this mime type (if necessary). We do not
  828.         // store anything in the mime type preferences for the ambiguous
  829.         // type application/octet-stream. We do NOT do this for 
  830.         // application/x-msdownload since we want users to be able to 
  831.         // autodownload these to disk. 
  832.         if (needUpdate && this.mLauncher.MIMEInfo.MIMEType != "application/octet-stream")
  833.           this.updateHelperAppPref();
  834.       } catch(e) { }
  835.  
  836.       // Unhook dialog from this object.
  837.       this.mDialog.dialog = null;
  838.  
  839.       // Close up dialog by returning true.
  840.       return true;
  841.     },
  842.  
  843.     // onCancel:
  844.     onCancel: function() {
  845.       // Remove our web progress listener.
  846.       this.mLauncher.setWebProgressListener(null);
  847.  
  848.       // Cancel app launcher.
  849.       try {
  850.         const NS_BINDING_ABORTED = 0x804b0002;
  851.         this.mLauncher.cancel(NS_BINDING_ABORTED);
  852.       } catch(exception) {
  853.       }
  854.  
  855.       // Unhook dialog from this object.
  856.       this.mDialog.dialog = null;
  857.  
  858.       // Close up dialog by returning true.
  859.       return true;
  860.     },
  861.  
  862.     // dialogElement:  Convenience. 
  863.     dialogElement: function(id) {
  864.       return this.mDialog.document.getElementById(id);
  865.     },
  866.  
  867.     // chooseApp:  Open file picker and prompt user for application.
  868.     chooseApp: function() {
  869.       var nsIFilePicker = Components.interfaces.nsIFilePicker;
  870.       var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
  871.       fp.init(this.mDialog,
  872.               this.dialogElement("strings").getString("chooseAppFilePickerTitle"),
  873.               nsIFilePicker.modeOpen);
  874.  
  875.       fp.appendFilters(nsIFilePicker.filterApps);
  876.  
  877.       if (fp.show() == nsIFilePicker.returnOK && fp.file) {
  878.         // Show the "handler" menulist since we have a (user-specified) 
  879.         // application now.
  880.         this.dialogElement("modeDeck").setAttribute("selectedIndex", "0");
  881.         
  882.         // Remember the file they chose to run.
  883.         var localHandlerApp = 
  884.           Components.classes["@mozilla.org/uriloader/local-handler-app;1"].
  885.           createInstance(Components.interfaces.nsILocalHandlerApp);
  886.         localHandlerApp.executable = fp.file;
  887.         this.chosenApp = localHandlerApp;
  888.         
  889.         // Update dialog.
  890.         var otherHandler = this.dialogElement("otherHandler");
  891.         otherHandler.removeAttribute("hidden");
  892.         otherHandler.setAttribute("path", this.getPath(this.chosenApp.executable));
  893.         otherHandler.label = this.chosenApp.executable.leafName;
  894.         this.dialogElement("openHandler").selectedIndex = 1;
  895.         this.dialogElement("openHandler").setAttribute("lastSelectedItemID", "otherHandler");
  896.         
  897.         this.dialogElement("mode").selectedItem = this.dialogElement("open");
  898.       }
  899.       else {
  900.         var openHandler = this.dialogElement("openHandler");
  901.         var lastSelectedID = openHandler.getAttribute("lastSelectedItemID");
  902.         if (!lastSelectedID)
  903.           lastSelectedID = "defaultHandler";
  904.         openHandler.selectedItem = this.dialogElement(lastSelectedID);
  905.       }
  906.     },
  907.  
  908.     // Turn this on to get debugging messages.
  909.     debug: false,
  910.  
  911.     // Dump text (if debug is on).
  912.     dump: function( text ) {
  913.         if ( this.debug ) {
  914.             dump( text ); 
  915.         }
  916.     },
  917.  
  918.     // dumpInfo:
  919.     doDebug: function() {
  920.         const nsIProgressDialog = Components.interfaces.nsIProgressDialog;
  921.         // Open new progress dialog.
  922.         var progress = Components.classes[ "@mozilla.org/progressdialog;1" ]
  923.                          .createInstance( nsIProgressDialog );
  924.         // Show it.
  925.         progress.open( this.mDialog );
  926.     },
  927.  
  928.     // dumpObj:
  929.     dumpObj: function( spec ) {
  930.          var val = "<undefined>";
  931.          try {
  932.              val = eval( "this."+spec ).toString();
  933.          } catch( exception ) {
  934.          }
  935.          this.dump( spec + "=" + val + "\n" );
  936.     },
  937.  
  938.     // dumpObjectProperties
  939.     dumpObjectProperties: function( desc, obj ) {
  940.          for( prop in obj ) {
  941.              this.dump( desc + "." + prop + "=" );
  942.              var val = "<undefined>";
  943.              try {
  944.                  val = obj[ prop ];
  945.              } catch ( exception ) {
  946.              }
  947.              this.dump( val + "\n" );
  948.          }
  949.     }
  950. }
  951.  
  952. // This Component's module implementation.  All the code below is used to get this
  953. // component registered and accessible via XPCOM.
  954. var module = {
  955.     firstTime: true,
  956.  
  957.     // registerSelf: Register this component.
  958.     registerSelf: function (compMgr, fileSpec, location, type) {
  959.         if (this.firstTime) {
  960.             this.firstTime = false;
  961.             throw Components.results.NS_ERROR_FACTORY_REGISTER_AGAIN;
  962.         }
  963.         compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
  964.  
  965.         compMgr.registerFactoryLocation( this.cid,
  966.                                          "Unknown Content Type Dialog",
  967.                                          this.contractId,
  968.                                          fileSpec,
  969.                                          location,
  970.                                          type );
  971.     },
  972.  
  973.     // getClassObject: Return this component's factory object.
  974.     getClassObject: function (compMgr, cid, iid) {
  975.         if (!cid.equals(this.cid)) {
  976.             throw Components.results.NS_ERROR_NO_INTERFACE;
  977.         }
  978.  
  979.         if (!iid.equals(Components.interfaces.nsIFactory)) {
  980.             throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  981.         }
  982.  
  983.         return this.factory;
  984.     },
  985.  
  986.     /* CID for this class */
  987.     cid: Components.ID("{F68578EB-6EC2-4169-AE19-8C6243F0ABE1}"),
  988.  
  989.     /* Contract ID for this class */
  990.     contractId: "@mozilla.org/helperapplauncherdialog;1",
  991.  
  992.     /* factory object */
  993.     factory: {
  994.         // createInstance: Return a new nsProgressDialog object.
  995.         createInstance: function (outer, iid) {
  996.             if (outer != null)
  997.                 throw Components.results.NS_ERROR_NO_AGGREGATION;
  998.  
  999.             return (new nsUnknownContentTypeDialog()).QueryInterface(iid);
  1000.         }
  1001.     },
  1002.  
  1003.     // canUnload: n/a (returns true)
  1004.     canUnload: function(compMgr) {
  1005.         return true;
  1006.     }
  1007. };
  1008.  
  1009. // NSGetModule: Return the nsIModule object.
  1010. function NSGetModule(compMgr, fileSpec) {
  1011.     return module;
  1012. }
  1013.